// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: support test of communication of simple-client
// feature: DServer support simple entity
// feature: provide query interfaces
// feature: discovery support client and server
// ------------------------------------------------------------------

#ifndef _EDDS_RTPS_DISCOVERY_PARTICIPANT_CHANGE_DATA_H_
#define _EDDS_RTPS_DISCOVERY_PARTICIPANT_CHANGE_DATA_H_

#include <edds/rtps/common/RemoteLocators.hpp>
#include <edds/dds/core/policy/ParameterTypes.hpp>

#include <nlohmann/json.hpp>
#include <discovery/database/backup/SharedBackupFunctions.hpp>
#include <deps/type/TypesBase.h>

#include <iomanip>
#include <sstream>
#include <string>

using vbs::common::types::DomainId_t;
using vbsutil::xmlparser::c_RTPSTimeInfinite;
using vbsutil::xmlparser::c_RTPSTimeInvalid;
using vbsutil::xmlparser::c_RTPSTimeZero;

namespace evbs {
namespace ertps {
namespace rtps {
namespace ddb {

inline Time_t string_to_Time_t(const std::string& time_str) {
    std::istringstream iss(time_str);
    std::tm time_info = {};
    char dot;
    uint32_t nanoseconds;

    iss >> std::get_time(&time_info, "%Y-%m-%dT%H:%M:%S") >> dot >> nanoseconds;

    if (iss.fail()) {
        throw std::invalid_argument("Failed to parse time string");
    }

    Time_t time;
    time.seconds(static_cast<int32_t>(std::mktime(&time_info)));
    time.nanosec(nanoseconds);
    return time;
}

inline std::string Time_t_to_string(const Time_t& time) {
    std::time_t raw_time = time.seconds();
    std::tm* time_info = std::localtime(&raw_time);

    std::ostringstream oss;
    oss << std::put_time(time_info, "%Y-%m-%dT%H:%M:%S");
    oss << "." << std::setw(9) << std::setfill('0') << time.nanosec();
    return oss.str();
}

inline Time_t divide_time_by_four(const Time_t& time) {
    int64_t total_ns = time.to_ns();

    int64_t divided_ns = total_ns >> 2;

    Time_t result;
    result.from_ns(divided_ns);

    return result;
}

/**
 * Class to join the main info required from a Participant in the Discovery Data Base
 *@ingroup DISCOVERY_MODULE
 */
class DiscoveryParticipantChangeData {
 public:
    DiscoveryParticipantChangeData()
        : metatraffic_locators_(RemoteLocatorList(5, 1)), transport_type_(TransportTypeKind::TRANSPORT_TYPE_AUTO) {}

    DiscoveryParticipantChangeData(RemoteLocatorList metatraffic_locators, bool is_client, bool is_local,
                                   bool is_simple, bool is_mvbs, bool is_compatible, uint16_t domain,
                                   const string_255& participant_name, Time_t start_time, Time_t announce,
                                   TransportTypeKind transport_type)
        : metatraffic_locators_(metatraffic_locators),
          is_client_(is_client),
          is_superclient_(false),
          is_local_(is_local),
          is_simple_(is_simple),
          is_mvbs_(is_mvbs),
          is_compatible_(is_compatible),
          domain_id_(domain),
          participant_name_(participant_name),
          start_time_(start_time),
          transport_type_(transport_type) {
        if (announce == c_RTPSTimeZero || announce == c_RTPSTimeInfinite || announce == c_RTPSTimeInvalid) {
            announce_ = {30, 0U};
        } else {
            announce_ = divide_time_by_four(announce);
        }
    }

    DiscoveryParticipantChangeData(RemoteLocatorList metatraffic_locators, bool is_client, bool is_local,
                                   bool is_simple, bool is_mvbs, bool is_compatible, uint16_t domain,
                                   const string_255& participant_name, const std::string& start_time,
                                   const int64_t& announce, const TransportTypeKind transport_type)
        : metatraffic_locators_(metatraffic_locators),
          is_client_(is_client),
          is_superclient_(false),
          is_local_(is_local),
          is_simple_(is_simple),
          is_mvbs_(is_mvbs),
          is_compatible_(is_compatible),
          domain_id_(domain),
          participant_name_(participant_name),
          start_time_(string_to_Time_t(start_time)),
          transport_type_(transport_type) {
        announce_.from_ns(announce);
    }

    DiscoveryParticipantChangeData(RemoteLocatorList metatraffic_locators, bool is_client, bool is_local,
                                   bool is_simple, bool is_mvbs, bool is_compatible, bool is_superclient,
                                   uint16_t domain, const string_255& participant_name, Time_t start_time,
                                   Time_t announce, TransportTypeKind transport_type)
        : metatraffic_locators_(metatraffic_locators),
          is_client_(is_client),
          is_superclient_(is_superclient),
          is_local_(is_local),
          is_simple_(is_simple),
          is_mvbs_(is_mvbs),
          is_compatible_(is_compatible),
          domain_id_(domain),
          participant_name_(participant_name),
          start_time_(start_time),
          transport_type_(transport_type) {
        if (announce == c_RTPSTimeZero || announce == c_RTPSTimeInfinite || announce == c_RTPSTimeInvalid) {
            announce_ = {30, 0U};
        } else {
            announce_ = divide_time_by_four(announce);
        }
    }

    bool is_client() const { return is_client_; }

    bool is_superclient() const { return is_superclient_; }

    bool is_local() const { return is_local_; }

    bool is_simple() const { return is_simple_; }

    bool is_mvbs() const { return is_mvbs_; }

    bool is_compatible() const { return is_compatible_; }

    DomainId_t domain_id() const { return domain_id_; }

    RemoteLocatorList metatraffic_locators() const { return metatraffic_locators_; }

    string_255 participant_name() const { return participant_name_; }

    std::string start_time() const { return Time_t_to_string(start_time_); }

    TransportTypeKind transport_type() const { return transport_type_; }
    Time_t announce() const { return announce_; }
    int64_t get_announce() const { return announce_.to_ns(); }

    void to_json(nlohmann::json& j) const {
        j["is_client"] = is_client_;
        j["is_superclient"] = is_superclient_;
        j["is_local"] = is_local_;
        j["is_simple"] = is_simple_;
        j["is_mvbs"] = is_mvbs_;
        j["is_compatible"] = is_compatible_;
        j["domain_id"] = domain_id_;
        j["participant_name"] = object_to_string(participant_name_);
        j["metatraffic_locators"] = object_to_string(metatraffic_locators_);
        j["start_time"] = Time_t_to_string(start_time_);
        j["transport_type"] = transport_type_;
        j["announce"] = announce_.to_ns();
    }

    void own_to_json(nlohmann::json& j, std::vector<SequenceNumber_t> sn_list) const {
        j["pdp_sn"] = object_to_string(sn_list[0]);
        j["edp_pub_sn"] = object_to_string(sn_list[1]);
        j["edp_sub_sn"] = object_to_string(sn_list[2]);
    }

 private:
    // The metatraffic locators of from the serialized payload
    RemoteLocatorList metatraffic_locators_;
    // Whether this participant is a CLIENT or a SERVER/BACKUP/SUPER_CLIENT
    // This variable affects the discovery filter to applied to each entity:
    // false => send all data ; true => send only data that is required to match endpoints
    bool is_client_ = false;
    // Wether this participant is a SUPER_CLIENT and needs special treatment
    // when matching remote endpoints
    bool is_superclient_ = false;
    // Whether this participant (CLIENT OR SERVER) is a client of this server
    bool is_local_ = false;
    bool is_simple_ = false;
    bool is_mvbs_ = false;
    bool is_compatible_ = false;
    DomainId_t domain_id_ = 0;
    string_255 participant_name_;
    Time_t start_time_;
    TransportTypeKind transport_type_;
    // 通告时间间隔，单位为s
    Time_t announce_;
};

} /* namespace ddb */
} /* namespace rtps */
} /* namespace ertps */
} /* namespace evbs */

#endif /* _EDDS_RTPS_PARTICIPANT_INFO_H_ */
